namespace hjc {

	export class AStarMap extends eui.Group {

		private $colNum: number;		//列数
		private $rowNum: number;		//行数
		private $nodeWidth: number;		//节点宽
		private $nodeHeight: number;	//节点高	
		private $allow8: boolean;		//允许八向寻路
		private $strategy: IAstarStrategy;//当前策略
		private $diagCost: number;		//对象线代价
		private $flagBits: number;		//标记位	
		private $searcher: AStarSearcher;//寻路器
		private $viewer: AStarViewer;	//显示器
		private $cache: boolean;		//缓存
		//Getter & Setters
		public get colNum(): number { return this.$colNum; }
		public get rowNum(): number { return this.$rowNum; }
		public get size(): number { return this.$colNum * this.$rowNum; }
		public get cache(): boolean { return this.$cache; }
		public get nodeWidth(): number { return this.$nodeWidth; }
		public get nodeHeight(): number { return this.$nodeHeight; }
		public set allow8(allow8: boolean) { this.$allow8 = allow8; }
		public get allow8(): boolean { return this.$allow8; }
		public get avgCost(): number { return this.$searcher.avgCost; }
		public get diagCost(): number { return this.$diagCost; }
		public set diagCost(diagCost: number) { this.$diagCost = diagCost; }
		public set strategy(strategy: IAstarStrategy) { this.$strategy = strategy; this.$strategy.map = this; }
		public get strategy(): IAstarStrategy { return this.$strategy; }
		public get viewer(): AStarViewer { return this.$viewer; }
		public constructor(config?: AStarMapConfig) {
			super();
			this._init();
			if (!!config) this.load(config);
		}
		//寻路
		public search(start: AStarNode, goal: AStarNode): AStarPath | null { return this.$searcher.search(start, goal); }
		//获得指定位置节点
		public getNode(col: number, row: number): AStarNode { return this.$searcher.getNode(col, row); }
		// //根据节点获得位置
		// public posOfNode(col: number, row: number): [number, number]
		// public posOfNode(node: AStarNode): [number, number]
		// public posOfNode(pa: AStarNode | number, pb?: number): [number, number] {
		// 	if (numeric(pa) && numeric(pb)) return [(<number>pa + 0.5) * this.$nodeWidth, (pb + 0.5) * this.$nodeHeight];
		// 	else if (pa instanceof AStarNode) return [(pa.col + 0.5) * this.$nodeWidth, (pa.row + 0.5) * this.$nodeHeight];
		// 	else { hjc.wrong('AStarMap.posOfNode', 'invalid form of params'); return [0, 0]; }
		// }
		//根据位置返回节点
		public nodeOfPos(entity: Entity): AStarNode;
		public nodeOfPos(x: number, y: number): AStarNode;
		public nodeOfPos(p1: Entity | number, p2?: number): AStarNode {
			if (numeric(p1) && numeric(p2)) return this.getNode(Math.floor(p1 as number / this.$nodeWidth), Math.floor(p2 / this.$nodeHeight));
			else if (p1 instanceof Entity) return this.getNode(Math.floor(p1.position.x / this.$nodeWidth), Math.floor(p1.position.y / this.$nodeHeight));
		}
		//设置所有节点数据
		public resetMapData(data: number): AStarMap {
			for (let row = 0; row < this.$rowNum; row++) {
				for (let col = 0; col < this.$colNum; col++) {
					this.resetNodeData(col, row, data);
				}
			}
			return this;
		}
		//设置指定节点数据
		public resetNodeData(col: number, row: number, data: number): AStarMap {
			let [index, node] = [row * this.$colNum + col, this.getNode(col, row)];
			let [x, y] = this._vector2(col, row);
			if (!!node && !!this.$viewer['$blocksLayer']) this.$viewer.redrawBlock(node.init(col, row, x, y, data));
			return this;
		}
		//返回当前节点数据配置
		public createConfig(): AStarMapConfig {
			let config: AStarMapConfig = {
				colNum: this.$colNum, rowNum: this.$rowNum,
				nodeWidth: this.$nodeWidth, nodeHeight: this.$nodeHeight,
				allow8: this.$allow8, data: []
			};
			for (let row = 0; row < this.$rowNum; row++) {
				for (let col = 0; col < this.$colNum; col++) {
					config.data.push(this.getNode(col, row).data);
				}
			}
			return config;
		}
		//展示网格层
		public showGrids(): AStarMap { this.$viewer.showGrids(); return this; }
		//隐藏网格层
		public hideGrids(): AStarMap { this.$viewer.hideGrids(); return this; }
		//展示地块层
		public showBlocks(): AStarMap { this.$viewer.showBlocks(); return this; }
		//展示地块层
		public hideBlocks(): AStarMap { this.$viewer.hideBlocks(); return this; }
		//显示路径层
		public showPaths(): AStarMap { this.$viewer.showPaths(); return this; }
		//隐藏路径层
		public hidePaths(): AStarMap { this.$viewer.hidePaths(); return this; }
		//清空地图数据
		public clear(): AStarMap {
			this.$colNum = this.$rowNum = this.$nodeWidth = this.$nodeHeight = 0;
			this.$searcher.clear(); return this;
		}
		//重设配置
		public load(config: AStarMapConfig): AStarMap {
			if (this._checkConfig(config)) {
				this.clear();
				this.name = !!config.name ? config.name : AStarViewer.DFT_NAME;
				this.$colNum = config.colNum;
				this.$rowNum = config.rowNum;
				this.$cache = config.cache !== false;
				if (!!config.strategy) {
					let Strategy = egret.getDefinitionByName(config.strategy);
					this.strategy = !!Strategy ? new Strategy() : new DiagonalStrategy();
				} else this.strategy = new DiagonalStrategy();
				this.$diagCost = Math.isPositive(config.diagCost) ? config.diagCost : this.$diagCost;
				this.$allow8 = (config.allow8 === false) ? false : true;
				this.$nodeWidth = utils.num(config.nodeSize, config.nodeWidth);
				this.$nodeHeight = utils.num(config.nodeSize, config.nodeHeight);
				this.width = this.$colNum * this.$nodeWidth;
				this.height = this.$rowNum * this.$nodeHeight;
				let loadData = notEmptyArr(config.data);
				for (let row = 0; row < this.$rowNum; row++) {
					for (let col = 0; col < this.$colNum; col++) {
						let [x, y] = this._vector2(col, row);
						if (loadData) {
							let index = row * this.$colNum + col;
							if (numeric(config.data[index])) this.$searcher.ucNode(col, row, x, y, config.data[index]);
							else hjc.wrong('AStarMap.load()', 'not valid number!', `data[${index}]`);
						} else this.$searcher.ucNode(col, row, x, y);
					}
				}
			}
			return this;
		}

		private _init(): void {
			this.touchEnabled = true;
			this.touchThrough = false;
			this.touchChildren = false;
			this.$searcher = new AStarSearcher(this);
			this.$viewer = new AStarViewer(this);
		}

		private _vector2(col: number, row: number): [number, number] {
			return [(col + 0.5) * this.$nodeWidth, (row + 0.5) * this.$nodeHeight];
		}

		private _checkConfig(config: AStarMapConfig): boolean {
			let func = 'AStarMap.load()';
			if (isEmpty(config)) { hjc.wrong(func, 'is empty'); return false; }
			if (!Math.isPosInt(config.colNum) || !Math.isPosInt(config.rowNum)) { hjc.require(func, 'positive integer', 'colNum', 'rowNum'); return false; }
			if (!Math.isPositive(config.nodeSize) && (!Math.isPositive(config.nodeWidth)
				|| !Math.isPositive(config.nodeHeight))) { hjc.require(func, 'positive number', 'nodeSize', 'nodeWidth&nodeHeight'); return false; }
			return true;
		}
	}
}